"""
网络监控模块
实现ping监控和TCP端口检测功能
"""

import socket
import statistics
import time
from typing import List, Optional, Tuple
import ping3

from ..models.device import NetworkMetrics, DeviceStatus
from ..utils.logger import get_logger

logger = get_logger(__name__)


class NetworkMonitor:
    """网络监控器"""
    
    def __init__(self, ping_timeout: int = 5, ping_count: int = 5):
        self.ping_timeout = ping_timeout
        self.ping_count = ping_count
        
    def ping_device(self, ip_address: str, count: Optional[int] = None) -> NetworkMetrics:
        """
        Ping设备并返回网络指标
        
        Args:
            ip_address: 目标IP地址
            count: ping包数量，默认使用初始化时的值
            
        Returns:
            NetworkMetrics: 网络性能指标
        """
        count = count or self.ping_count
        results = []
        lost_packets = 0
        
        logger.debug(f"开始ping {ip_address}，包数量: {count}")
        
        for i in range(count):
            try:
                # 使用ping3库进行ping测试
                response_time = ping3.ping(ip_address, timeout=self.ping_timeout)
                
                if response_time is not None:
                    # 转换为毫秒
                    latency_ms = response_time * 1000
                    results.append(latency_ms)
                    logger.debug(f"ping {ip_address} #{i+1}: {latency_ms:.2f}ms")
                else:
                    lost_packets += 1
                    logger.debug(f"ping {ip_address} #{i+1}: 超时")
                    
            except Exception as e:
                lost_packets += 1
                logger.warning(f"ping {ip_address} #{i+1} 失败: {e}")
        
        # 计算网络指标
        if results:
            avg_latency = statistics.mean(results)
            jitter = statistics.stdev(results) if len(results) > 1 else 0.0
        else:
            avg_latency = 0.0
            jitter = 0.0
        
        packet_loss_rate = (lost_packets / count) * 100
        
        metrics = NetworkMetrics(
            latency_ms=avg_latency,
            packet_loss_rate=packet_loss_rate,
            jitter_ms=jitter
        )
        
        logger.info(f"ping {ip_address} 完成 - 延迟: {avg_latency:.2f}ms, 丢包率: {packet_loss_rate:.1f}%, 抖动: {jitter:.2f}ms")
        return metrics
    
    def check_tcp_port(self, ip_address: str, port: int, timeout: int = 5) -> bool:
        """
        检查TCP端口连通性
        
        Args:
            ip_address: 目标IP地址
            port: 目标端口
            timeout: 连接超时时间
            
        Returns:
            bool: 端口是否可达
        """
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            
            start_time = time.time()
            result = sock.connect_ex((ip_address, port))
            end_time = time.time()
            
            sock.close()
            
            is_open = result == 0
            response_time = (end_time - start_time) * 1000
            
            if is_open:
                logger.debug(f"端口 {ip_address}:{port} 开放 ({response_time:.2f}ms)")
            else:
                logger.debug(f"端口 {ip_address}:{port} 关闭或不可达")
                
            return is_open
            
        except Exception as e:
            logger.warning(f"检查端口 {ip_address}:{port} 时出错: {e}")
            return False
    
    def check_multiple_ports(self, ip_address: str, ports: List[int], timeout: int = 5) -> List[int]:
        """
        检查多个TCP端口的连通性
        
        Args:
            ip_address: 目标IP地址
            ports: 端口列表
            timeout: 连接超时时间
            
        Returns:
            List[int]: 开放的端口列表
        """
        open_ports = []
        
        for port in ports:
            if self.check_tcp_port(ip_address, port, timeout):
                open_ports.append(port)
        
        logger.info(f"{ip_address} 开放端口: {open_ports}")
        return open_ports
    
    def assess_device_status(self, ip_address: str, network_metrics: NetworkMetrics, 
                           open_ports: List[int]) -> DeviceStatus:
        """
        根据网络指标和端口状态评估设备状态
        
        Args:
            ip_address: 设备IP地址
            network_metrics: 网络性能指标
            open_ports: 开放的端口列表
            
        Returns:
            DeviceStatus: 设备状态
        """
        # 完全无法ping通
        if network_metrics.packet_loss_rate == 100:
            logger.info(f"设备 {ip_address} 状态: 离线 (100% 丢包)")
            return DeviceStatus.OFFLINE
        
        # 丢包率过高，网络有问题
        if network_metrics.packet_loss_rate > 50:
            logger.info(f"设备 {ip_address} 状态: 网络异常 (丢包率: {network_metrics.packet_loss_rate:.1f}%)")
            return DeviceStatus.NETWORK_ISSUE
        
        # 网络通但没有开放的服务端口
        if not open_ports:
            logger.info(f"设备 {ip_address} 状态: 服务异常 (无开放端口)")
            return DeviceStatus.SERVICE_ISSUE
        
        # 网络正常且有服务端口开放
        logger.info(f"设备 {ip_address} 状态: 在线")
        return DeviceStatus.ONLINE
    
    def comprehensive_check(self, ip_address: str, ports: List[int]) -> Tuple[DeviceStatus, NetworkMetrics, List[int]]:
        """
        综合检查设备状态
        
        Args:
            ip_address: 设备IP地址
            ports: 要检查的端口列表
            
        Returns:
            Tuple[DeviceStatus, NetworkMetrics, List[int]]: 设备状态、网络指标、开放端口
        """
        logger.info(f"开始综合检查设备: {ip_address}")
        
        # 执行ping测试
        network_metrics = self.ping_device(ip_address)
        
        # 检查端口连通性
        open_ports = self.check_multiple_ports(ip_address, ports)
        
        # 评估设备状态
        status = self.assess_device_status(ip_address, network_metrics, open_ports)
        
        logger.info(f"设备 {ip_address} 综合检查完成 - 状态: {status.value}")
        return status, network_metrics, open_ports


class DeviceMonitor:
    """设备监控器"""
    
    def __init__(self, ping_timeout: int = 5, ping_count: int = 5, port_timeout: int = 5):
        self.network_monitor = NetworkMonitor(ping_timeout, ping_count)
        self.port_timeout = port_timeout
        self.default_ports = [80, 8080, 554, 8554]  # 常用ONVIF端口
        
    def monitor_device(self, ip_address: str, ports: Optional[List[int]] = None) -> dict:
        """
        监控单个设备
        
        Args:
            ip_address: 设备IP地址
            ports: 要检查的端口列表，默认使用常用ONVIF端口
            
        Returns:
            dict: 监控结果
        """
        ports = ports or self.default_ports
        
        try:
            status, metrics, open_ports = self.network_monitor.comprehensive_check(ip_address, ports)
            
            result = {
                'ip_address': ip_address,
                'status': status.value,
                'network_metrics': metrics.to_dict(),
                'open_ports': open_ports,
                'timestamp': metrics.timestamp.isoformat(),
                'success': True,
                'error_message': ''
            }
            
            return result
            
        except Exception as e:
            logger.error(f"监控设备 {ip_address} 时出错: {e}")
            
            return {
                'ip_address': ip_address,
                'status': DeviceStatus.UNKNOWN.value,
                'network_metrics': None,
                'open_ports': [],
                'timestamp': time.time(),
                'success': False,
                'error_message': str(e)
            }
    
    def monitor_multiple_devices(self, ip_addresses: List[str], 
                               ports: Optional[List[int]] = None) -> List[dict]:
        """
        监控多个设备
        
        Args:
            ip_addresses: 设备IP地址列表
            ports: 要检查的端口列表
            
        Returns:
            List[dict]: 监控结果列表
        """
        results = []
        
        for ip_address in ip_addresses:
            result = self.monitor_device(ip_address, ports)
            results.append(result)
        
        # 统计结果
        online_count = sum(1 for r in results if r['status'] == DeviceStatus.ONLINE.value)
        offline_count = sum(1 for r in results if r['status'] == DeviceStatus.OFFLINE.value)
        
        logger.info(f"批量监控完成 - 总数: {len(results)}, 在线: {online_count}, 离线: {offline_count}")
        
        return results
